From: Florian Müllner Date: Tue, 27 Jun 2023 18:27:53 +0000 (+0200) Subject: tests: Stop bundling "perf" tests with gnome-shell X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~337^2~1^2~171 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=e7d290bbfb976bb857d47f2b099b0dfd2abb9068;p=gnome-shell.git tests: Stop bundling "perf" tests with gnome-shell Now that scripts are loaded as external modules, there's no reason anymore for bundling them with the gnome-shell executable. Just move the scripts into a dedicated folder in tests/ and run them from there. Part-of: --- diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml index c6e479150..9c2c8881f 100644 --- a/js/js-resources.gresource.xml +++ b/js/js-resources.gresource.xml @@ -35,12 +35,6 @@ misc/util.js misc/weather.js - perf/basic.js - perf/closeWithActiveWindows.js - perf/core.js - perf/headlessStart.js - perf/hwtest.js - ui/accessDialog.js ui/altTab.js ui/animation.js diff --git a/js/perf/basic.js b/js/perf/basic.js deleted file mode 100644 index ef75ca423..000000000 --- a/js/perf/basic.js +++ /dev/null @@ -1,156 +0,0 @@ -// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -/* eslint camelcase: ["error", { properties: "never", allow: ["^script_"] }] */ - -const St = imports.gi.St; - -const Main = imports.ui.main; -const MessageTray = imports.ui.messageTray; -const Scripting = imports.ui.scripting; - -// This script tests the most important (basic) functionality of the shell. - -export var METRICS = {}; - -export async function run() { - console.debug('Running basic perf test'); - - /* eslint-disable no-await-in-loop */ - Scripting.defineScriptEvent('topBarNavStart', 'Starting to navigate the top bar'); - Scripting.defineScriptEvent('topBarNavDone', 'Done navigating the top bar'); - Scripting.defineScriptEvent('notificationShowStart', 'Showing a notification'); - Scripting.defineScriptEvent('notificationShowDone', 'Done showing a notification'); - Scripting.defineScriptEvent('notificationCloseStart', 'Closing a notification'); - Scripting.defineScriptEvent('notificationCloseDone', 'Done closing a notification'); - Scripting.defineScriptEvent('overviewShowStart', 'Starting to show the overview'); - Scripting.defineScriptEvent('overviewShowDone', 'Overview finished showing'); - Scripting.defineScriptEvent('applicationsShowStart', 'Starting to switch to applications view'); - Scripting.defineScriptEvent('applicationsShowDone', 'Done switching to applications view'); - - Main.overview.connect('shown', - () => Scripting.scriptEvent('overviewShowDone')); - - await Scripting.sleep(1000); - - // navigate through top bar - - console.debug('Navigate through top bar'); - Scripting.scriptEvent('topBarNavStart'); - Main.panel.statusArea.quickSettings.menu.open(); - await Scripting.sleep(400); - - const { menuManager } = Main.panel; - while (menuManager.activeMenu && - Main.panel.navigate_focus(menuManager.activeMenu.sourceActor, - St.DirectionType.TAB_BACKWARD, false)) - await Scripting.sleep(400); - Scripting.scriptEvent('topBarNavDone'); - - await Scripting.sleep(1000); - - // notification - console.debug('Show notification message'); - const source = new MessageTray.SystemNotificationSource(); - Main.messageTray.add(source); - - Scripting.scriptEvent('notificationShowStart'); - source.connect('notification-show', - () => Scripting.scriptEvent('notificationShowDone')); - - const notification = new MessageTray.Notification(source, - 'A test notification'); - source.showNotification(notification); - await Scripting.sleep(400); - - console.debug('Show date menu'); - Main.panel.statusArea.dateMenu.menu.open(); - await Scripting.sleep(400); - - Scripting.scriptEvent('notificationCloseStart'); - notification.connect('destroy', - () => Scripting.scriptEvent('notificationCloseDone')); - - console.debug('Destroy notification message'); - notification.destroy(); - await Scripting.sleep(400); - - console.debug('Close date menu'); - Main.panel.statusArea.dateMenu.menu.close(); - await Scripting.waitLeisure(); - - await Scripting.sleep(1000); - - // overview (window picker) - Scripting.scriptEvent('overviewShowStart'); - Main.overview.show(); - await Scripting.waitLeisure(); - Main.overview.hide(); - await Scripting.waitLeisure(); - - await Scripting.sleep(1000); - - // overview (app picker) - console.debug('Showing overview'); - Main.overview.show(); - await Scripting.waitLeisure(); - - Scripting.scriptEvent('applicationsShowStart'); - console.debug('Showing applications'); - // eslint-disable-next-line require-atomic-updates - Main.overview.dash.showAppsButton.checked = true; - await Scripting.waitLeisure(); - Scripting.scriptEvent('applicationsShowDone'); - console.debug('Hiding applications'); - // eslint-disable-next-line require-atomic-updates - Main.overview.dash.showAppsButton.checked = false; - await Scripting.waitLeisure(); - - console.debug('Hiding overview'); - Main.overview.hide(); - await Scripting.waitLeisure(); - /* eslint-enable no-await-in-loop */ - - console.debug('Finished basic perf test'); -} - -let topBarNav = false; -let notificationShown = false; -let notificationClosed = false; -let windowPickerShown = false; -let appPickerShown = false; - -export function script_topBarNavDone() { - topBarNav = true; -} - -export function script_notificationShowDone() { - notificationShown = true; -} - -export function script_notificationCloseDone() { - notificationClosed = true; -} - -export function script_overviewShowDone() { - windowPickerShown = true; -} - -export function script_applicationsShowDone() { - appPickerShown = true; -} - -export function finish() { - if (!topBarNav) - throw new Error('Failed to navigate top bar'); - - if (!notificationShown) - throw new Error('Failed to show notification'); - - if (!notificationClosed) - throw new Error('Failed to close notification'); - - if (!windowPickerShown) - throw new Error('Failed to show window picker'); - - if (!appPickerShown) - throw new Error('Failed to show app picker'); -} diff --git a/js/perf/closeWithActiveWindows.js b/js/perf/closeWithActiveWindows.js deleted file mode 100644 index 2d696792c..000000000 --- a/js/perf/closeWithActiveWindows.js +++ /dev/null @@ -1,35 +0,0 @@ -// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^malloc", "^glx", "^clutter"] }] */ - -const Clutter = imports.gi.Clutter; - -const Main = imports.ui.main; -const Scripting = imports.ui.scripting; - -/** Run test. */ -export async function run() { - /* eslint-disable no-await-in-loop */ - - /* Make created windows remain visible during exit. */ - await Scripting.disableHelperAutoExit(); - - const seat = Clutter.get_default_backend().get_default_seat(); - const virtualDevice_ = - seat.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE); - - Main.overview.hide(); - await Scripting.waitLeisure(); - await Scripting.sleep(1000); - - await Scripting.createTestWindow({ - width: 640, - height: 480, - textInput: true, - }); - - await Scripting.waitTestWindows(); - await Scripting.waitLeisure(); - await Scripting.sleep(1000); - - /* eslint-enable no-await-in-loop */ -} diff --git a/js/perf/core.js b/js/perf/core.js deleted file mode 100644 index 78d17d8c9..000000000 --- a/js/perf/core.js +++ /dev/null @@ -1,267 +0,0 @@ -// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^malloc", "^glx", "^clutter"] }] */ - -const System = imports.system; - -const Main = imports.ui.main; -const Scripting = imports.ui.scripting; - -// This performance script measure the most important (core) performance -// metrics for the shell. By looking at the output metrics of this script -// someone should be able to get an idea of how well the shell is performing -// on a particular system. - -export var METRICS = { - overviewLatencyFirst: { - description: 'Time to first frame after triggering overview, first time', - units: 'us', - }, - overviewFpsFirst: { - description: 'Frame rate when going to the overview, first time', - units: 'frames / s', - }, - overviewLatencySubsequent: { - description: 'Time to first frame after triggering overview, second time', - units: 'us', - }, - overviewFpsSubsequent: { - description: 'Frames rate when going to the overview, second time', - units: 'frames / s', - }, - overviewFps5Windows: { - description: 'Frames rate when going to the overview, 5 windows open', - units: 'frames / s', - }, - overviewFps10Windows: { - description: 'Frames rate when going to the overview, 10 windows open', - units: 'frames / s', - }, - overviewFps5Maximized: { - description: 'Frames rate when going to the overview, 5 maximized windows open', - units: 'frames / s', - }, - overviewFps10Maximized: { - description: 'Frames rate when going to the overview, 10 maximized windows open', - units: 'frames / s', - }, - overviewFps5Alpha: { - description: 'Frames rate when going to the overview, 5 alpha-transparent windows open', - units: 'frames / s', - }, - overviewFps10Alpha: { - description: 'Frames rate when going to the overview, 10 alpha-transparent windows open', - units: 'frames / s', - }, - usedAfterOverview: { - description: "Malloc'ed bytes after the overview is shown once", - units: 'B', - }, - leakedAfterOverview: { - description: "Additional malloc'ed bytes the second time the overview is shown", - units: 'B', - }, - applicationsShowTimeFirst: { - description: 'Time to switch to applications view, first time', - units: 'us', - }, - applicationsShowTimeSubsequent: { - description: 'Time to switch to applications view, second time', - units: 'us', - }, -}; - -const WINDOW_CONFIGS = [{ - width: 640, height: 480, - alpha: false, maximized: false, count: 1, metric: 'overviewFpsSubsequent', -}, { - width: 640, height: 480, - alpha: false, maximized: false, count: 5, metric: 'overviewFps5Windows', -}, { - width: 640, height: 480, - alpha: false, maximized: false, count: 10, metric: 'overviewFps10Windows', -}, { - width: 640, height: 480, - alpha: false, maximized: true, count: 5, metric: 'overviewFps5Maximized', -}, { - width: 640, height: 480, - alpha: false, maximized: true, count: 10, metric: 'overviewFps10Maximized', -}, { - width: 640, height: 480, - alpha: true, maximized: false, count: 5, metric: 'overviewFps5Alpha', -}, { - width: 640, height: 480, - alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha', -}]; - -export async function run() { - /* eslint-disable no-await-in-loop */ - Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview"); - Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing"); - Scripting.defineScriptEvent("afterShowHide", "After a show/hide cycle for the overview"); - Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view"); - Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view"); - - // Enable recording of timestamps for different points in the frame cycle - global.frame_timestamps = true; - - Main.overview.connect('shown', () => { - Scripting.scriptEvent('overviewShowDone'); - }); - - await Scripting.sleep(1000); - - for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) { - // We go to the overview twice for each configuration; the first time - // to calculate the mipmaps for the windows, the second time to get - // a clean set of numbers. - if ((i % 2) == 0) { - let config = WINDOW_CONFIGS[i / 2]; - await Scripting.destroyTestWindows(); - - for (let k = 0; k < config.count; k++) { - await Scripting.createTestWindow({ - width: config.width, - height: config.height, - alpha: config.alpha, - maximized: config.maximized, - }); - } - - await Scripting.waitTestWindows(); - await Scripting.sleep(1000); - await Scripting.waitLeisure(); - } - - Scripting.scriptEvent('overviewShowStart'); - Main.overview.show(); - - await Scripting.waitLeisure(); - Main.overview.hide(); - await Scripting.waitLeisure(); - - System.gc(); - await Scripting.sleep(1000); - Scripting.collectStatistics(); - Scripting.scriptEvent('afterShowHide'); - } - - await Scripting.destroyTestWindows(); - await Scripting.sleep(1000); - - Main.overview.show(); - await Scripting.waitLeisure(); - - for (let i = 0; i < 2; i++) { - Scripting.scriptEvent('applicationsShowStart'); - // eslint-disable-next-line require-atomic-updates - Main.overview.dash.showAppsButton.checked = true; - await Scripting.waitLeisure(); - Scripting.scriptEvent('applicationsShowDone'); - // eslint-disable-next-line require-atomic-updates - Main.overview.dash.showAppsButton.checked = false; - await Scripting.waitLeisure(); - } - /* eslint-enable no-await-in-loop */ -} - -let showingOverview = false; -let finishedShowingOverview = false; -let overviewShowStart; -let overviewFrames; -let overviewLatency; -let mallocUsedSize = 0; -let overviewShowCount = 0; -let haveSwapComplete = false; -let applicationsShowStart; -let applicationsShowCount = 0; - -export function script_overviewShowStart(time) { - showingOverview = true; - finishedShowingOverview = false; - overviewShowStart = time; - overviewFrames = 0; -} - -export function script_overviewShowDone(_time) { - // We've set up the state at the end of the zoom out, but we - // need to wait for one more frame to paint before we count - // ourselves as done. - finishedShowingOverview = true; -} - -export function script_applicationsShowStart(time) { - applicationsShowStart = time; -} - -export function script_applicationsShowDone(time) { - applicationsShowCount++; - if (applicationsShowCount == 1) - METRICS.applicationsShowTimeFirst.value = time - applicationsShowStart; - else - METRICS.applicationsShowTimeSubsequent.value = time - applicationsShowStart; -} - -export function script_afterShowHide(_time) { - if (overviewShowCount == 1) - METRICS.usedAfterOverview.value = mallocUsedSize; - else - METRICS.leakedAfterOverview.value = mallocUsedSize - METRICS.usedAfterOverview.value; -} - -export function malloc_usedSize(time, bytes) { - mallocUsedSize = bytes; -} - -function _frameDone(time) { - if (showingOverview) { - if (overviewFrames == 0) - overviewLatency = time - overviewShowStart; - - overviewFrames++; - } - - if (finishedShowingOverview) { - showingOverview = false; - finishedShowingOverview = false; - overviewShowCount++; - - let dt = (time - (overviewShowStart + overviewLatency)) / 1000000; - - // If we see a start frame and an end frame, that would - // be 1 frame for a FPS computation, hence the '- 1' - let fps = (overviewFrames - 1) / dt; - - if (overviewShowCount == 1) { - METRICS.overviewLatencyFirst.value = overviewLatency; - METRICS.overviewFpsFirst.value = fps; - } else if (overviewShowCount == 2) { - METRICS.overviewLatencySubsequent.value = overviewLatency; - } - - // Other than overviewFpsFirst, we collect FPS metrics the second - // we show each window configuration. overviewShowCount is 1,2,3... - if (overviewShowCount % 2 == 0) { - let config = WINDOW_CONFIGS[(overviewShowCount / 2) - 1]; - METRICS[config.metric].value = fps; - } - } -} - -export function glx_swapComplete(time, swapTime) { - haveSwapComplete = true; - - _frameDone(swapTime); -} - -export function clutter_stagePaintDone(time) { - // If we aren't receiving GLXBufferSwapComplete events, then we approximate - // the time the user sees a frame with the time we finished doing drawing - // commands for the frame. This doesn't take into account the time for - // the GPU to finish painting, and the time for waiting for the buffer - // swap, but if this are uniform - every frame takes the same time to draw - - // then it won't upset our FPS calculation, though the latency value - // will be slightly too low. - - if (!haveSwapComplete) - _frameDone(time); -} diff --git a/js/perf/headlessStart.js b/js/perf/headlessStart.js deleted file mode 100644 index 6f4157b2b..000000000 --- a/js/perf/headlessStart.js +++ /dev/null @@ -1,107 +0,0 @@ -// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- -/* eslint camelcase: ["error", { properties: "never", allow: ["^script_"] }] */ - -const GLib = imports.gi.GLib; -const MetaTest = imports.gi.MetaTest; -const Shell = imports.gi.Shell; - -const Main = imports.ui.main; -const Scripting = imports.ui.scripting; - -// This script tests that the shell handles connecting monitors after startup -// is properly handled. - -export var METRICS = {}; - -let _testMonitor = null; - -/** - * init: - */ -export function init() { - global.connect('shutdown', () => { - _testMonitor?.destroy(); - }); - GLib.timeout_add_seconds( - GLib.PRIORITY_LOW, 2, - () => { - log('Connecting 1280x720 test monitor'); - _testMonitor = MetaTest.TestMonitor.new( - global.context, 1280, 720, 60.0); - }); - Scripting.defineScriptEvent('monitorsChanged', 'Monitors changed'); - - const display = global.display; - console.assert(display.get_n_monitors() === 0, 'Not running headless'); - - const monitorManager = global.backend.get_monitor_manager(); - const monitorsChangedHandlerId = monitorManager.connect('monitors-changed', - () => { - console.assert(display.get_n_monitors() === 1, - 'Expected a single monitor'); - Scripting.scriptEvent('monitorsChanged'); - monitorManager.disconnect(monitorsChangedHandlerId); - }); - Shell.PerfLog.get_default().set_enabled(true); -} - -/** - * run: - */ -export async function run() { - /* eslint-disable no-await-in-loop */ - Scripting.defineScriptEvent('overviewShowDone', 'Overview finished showing'); - Scripting.defineScriptEvent('overviewHideDone', 'Overview finished hiding'); - - Main.overview.connect('shown', - () => Scripting.scriptEvent('overviewShowDone')); - Main.overview.connect('hidden', - () => Scripting.scriptEvent('overviewHideDone')); - - Main.overview.hide(); - await Scripting.waitLeisure(); - - Main.overview.show(); - await Scripting.waitLeisure(); - - /* eslint-enable no-await-in-loop */ -} - -let monitorsChanged = false; -let overviewHidden = false; -let overviewShown = false; - -/** - * script_monitorsChanged: - */ -export function script_monitorsChanged() { - monitorsChanged = true; -} - -/** - * script_overviewHideDone: - */ -export function script_overviewHideDone() { - overviewHidden = true; -} - -/** - * script_overviewShowDone: - */ -export function script_overviewShowDone() { - overviewShown = true; -} - -/** - * finish: - */ -export function finish() { - if (!monitorsChanged) - throw new Error('Monitors never changed'); - - if (!overviewHidden) - throw new Error('Failed to hide overview'); - - if (!overviewShown) - throw new Error('Failed to show overview'); -} diff --git a/js/perf/hwtest.js b/js/perf/hwtest.js deleted file mode 100644 index 6e4cd7ea2..000000000 --- a/js/perf/hwtest.js +++ /dev/null @@ -1,313 +0,0 @@ -/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^clutter"] }] */ -const Clutter = imports.gi.Clutter; -const Gio = imports.gi.Gio; -const Shell = imports.gi.Shell; -const Main = imports.ui.main; -const Scripting = imports.ui.scripting; - -export var METRICS = { - timeToDesktop: { - description: 'Time from starting graphical.target to desktop showing', - units: 'us', - }, - - overviewShowTime: { - description: 'Time to switch to overview view, first time', - units: 'us', - }, - - applicationsShowTime: { - description: 'Time to switch to applications view, first time', - units: 'us', - }, - - mainViewRedrawTime: { - description: 'Time to redraw the main view, full screen', - units: 'us', - }, - - overviewRedrawTime: { - description: 'Time to redraw the overview, full screen, 5 windows', - units: 'us', - }, - - applicationRedrawTime: { - description: 'Time to redraw frame with a maximized application update', - units: 'us', - }, - - geditStartTime: { - description: 'Time from gedit launch to window drawn', - units: 'us', - }, -}; - -function waitAndDraw(milliseconds) { - let cb; - - let timeline = new Clutter.Timeline({ duration: milliseconds }); - timeline.start(); - - timeline.connect('new-frame', (_timeline, _frame) => { - global.stage.queue_redraw(); - }); - - timeline.connect('completed', () => { - timeline.stop(); - if (cb) - cb(); - }); - - return callback => (cb = callback); -} - -function waitSignal(object, signal) { - let cb; - - let id = object.connect(signal, () => { - object.disconnect(id); - if (cb) - cb(); - }); - - return callback => (cb = callback); -} - -function extractBootTimestamp() { - const sp = Gio.Subprocess.new([ - 'journalctl', '-b', - 'MESSAGE_ID=7d4958e842da4a758f6c1cdc7b36dcc5', - 'UNIT=graphical.target', - '-o', - 'json', - ], Gio.SubprocessFlags.STDOUT_PIPE); - let result = null; - - let datastream = Gio.DataInputStream.new(sp.get_stdout_pipe()); - while (true) { // eslint-disable-line no-constant-condition - let [line, length_] = datastream.read_line_utf8(null); - if (line === null) - break; - - let fields = JSON.parse(line); - result = Number(fields['__MONOTONIC_TIMESTAMP']); - } - datastream.close(null); - return result; -} - -export async function run() { - /* eslint-disable no-await-in-loop */ - Scripting.defineScriptEvent("desktopShown", "Finished initial animation"); - Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview"); - Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing"); - Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view"); - Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view"); - Scripting.defineScriptEvent("mainViewDrawStart", "Drawing main view"); - Scripting.defineScriptEvent("mainViewDrawDone", "Ending timing main view drawing"); - Scripting.defineScriptEvent("overviewDrawStart", "Drawing overview"); - Scripting.defineScriptEvent("overviewDrawDone", "Ending timing overview drawing"); - Scripting.defineScriptEvent("redrawTestStart", "Drawing application window"); - Scripting.defineScriptEvent("redrawTestDone", "Ending timing application window drawing"); - Scripting.defineScriptEvent("collectTimings", "Accumulate frame timings from redraw tests"); - Scripting.defineScriptEvent("geditLaunch", "gedit application launch"); - Scripting.defineScriptEvent("geditFirstFrame", "first frame of gedit window drawn"); - - await Scripting.waitLeisure(); - Scripting.scriptEvent('desktopShown'); - - let interfaceSettings = new Gio.Settings({ - schema_id: 'org.gnome.desktop.interface', - }); - interfaceSettings.set_boolean('enable-animations', false); - - Scripting.scriptEvent('overviewShowStart'); - Main.overview.show(); - await Scripting.waitLeisure(); - Scripting.scriptEvent('overviewShowDone'); - - await Scripting.sleep(1000); - - Scripting.scriptEvent('applicationsShowStart'); - // eslint-disable-next-line require-atomic-updates - Main.overview.dash.showAppsButton.checked = true; - - await Scripting.waitLeisure(); - Scripting.scriptEvent('applicationsShowDone'); - - await Scripting.sleep(1000); - - Main.overview.hide(); - await Scripting.waitLeisure(); - - // --------------------- // - // Tests of redraw speed // - // --------------------- // - - global.frame_timestamps = true; - global.frame_finish_timestamp = true; - - for (let k = 0; k < 5; k++) - await Scripting.createTestWindow({ maximized: true }); - await Scripting.waitTestWindows(); - - await Scripting.sleep(1000); - - Scripting.scriptEvent('mainViewDrawStart'); - await waitAndDraw(1000); - Scripting.scriptEvent('mainViewDrawDone'); - - Main.overview.show(); - Scripting.waitLeisure(); - - await Scripting.sleep(1500); - - Scripting.scriptEvent('overviewDrawStart'); - await waitAndDraw(1000); - Scripting.scriptEvent('overviewDrawDone'); - - await Scripting.destroyTestWindows(); - Main.overview.hide(); - - await Scripting.createTestWindow({ - maximized: true, - redraws: true, - }); - await Scripting.waitTestWindows(); - - await Scripting.sleep(1000); - - Scripting.scriptEvent('redrawTestStart'); - await Scripting.sleep(1000); - Scripting.scriptEvent('redrawTestDone'); - - await Scripting.sleep(1000); - Scripting.scriptEvent('collectTimings'); - - await Scripting.destroyTestWindows(); - - global.frame_timestamps = false; - global.frame_finish_timestamp = false; - - await Scripting.sleep(1000); - - let appSys = Shell.AppSystem.get_default(); - let app = appSys.lookup_app('org.gnome.gedit.desktop'); - - Scripting.scriptEvent('geditLaunch'); - app.activate(); - - let windows = app.get_windows(); - if (windows.length > 0) - throw new Error('gedit was already running'); - - while (windows.length == 0) { - await waitSignal(global.display, 'window-created'); - windows = app.get_windows(); - } - - let actor = windows[0].get_compositor_private(); - await waitSignal(actor, 'first-frame'); - Scripting.scriptEvent('geditFirstFrame'); - - await Scripting.sleep(1000); - - windows[0].delete(global.get_current_time()); - - await Scripting.sleep(1000); - - interfaceSettings.set_boolean('enable-animations', true); - /* eslint-enable no-await-in-loop */ -} - -let overviewShowStart; -let applicationsShowStart; -let stagePaintStart; -let redrawTiming; -let redrawTimes = {}; -let geditLaunchTime; - -export function script_desktopShown(time) { - let bootTimestamp = extractBootTimestamp(); - METRICS.timeToDesktop.value = time - bootTimestamp; -} - -export function script_overviewShowStart(time) { - overviewShowStart = time; -} - -export function script_overviewShowDone(time) { - METRICS.overviewShowTime.value = time - overviewShowStart; -} - -export function script_applicationsShowStart(time) { - applicationsShowStart = time; -} - -export function script_applicationsShowDone(time) { - METRICS.applicationsShowTime.value = time - applicationsShowStart; -} - -export function script_mainViewDrawStart(_time) { - redrawTiming = 'mainView'; -} - -export function script_mainViewDrawDone(_time) { - redrawTiming = null; -} - -export function script_overviewDrawStart(_time) { - redrawTiming = 'overview'; -} - -export function script_overviewDrawDone(_time) { - redrawTiming = null; -} - -export function script_redrawTestStart(_time) { - redrawTiming = 'application'; -} - -export function script_redrawTestDone(_time) { - redrawTiming = null; -} - -export function script_collectTimings(_time) { - for (let timing in redrawTimes) { - let times = redrawTimes[timing]; - times.sort((a, b) => a - b); - - let len = times.length; - let median; - - if (len == 0) - median = -1; - else if (len % 2 == 1) - median = times[(len - 1) / 2]; - else - median = Math.round((times[len / 2 - 1] + times[len / 2]) / 2); - - METRICS[`${timing}RedrawTime`].value = median; - } -} - -export function script_geditLaunch(time) { - geditLaunchTime = time; -} - -export function script_geditFirstFrame(time) { - METRICS.geditStartTime.value = time - geditLaunchTime; -} - -export function clutter_stagePaintStart(time) { - stagePaintStart = time; -} - -export function clutter_paintCompletedTimestamp(time) { - if (redrawTiming != null && stagePaintStart != null) { - if (!(redrawTiming in redrawTimes)) - redrawTimes[redrawTiming] = []; - redrawTimes[redrawTiming].push(time - stagePaintStart); - } - stagePaintStart = null; -} diff --git a/tests/meson.build b/tests/meson.build index 49cc3b1c2..d54bdbbbf 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -72,7 +72,7 @@ foreach shell_test : shell_tests test_tool, '--headless', options, - '@0@/js/perf/@1@.js'.format(meson.project_source_root(), test_name), + '@0@/shell/@1@.js'.format(meson.current_source_dir(), test_name), ], is_parallel: false, env: shell_testenv, diff --git a/tests/shell/basic.js b/tests/shell/basic.js new file mode 100644 index 000000000..ef75ca423 --- /dev/null +++ b/tests/shell/basic.js @@ -0,0 +1,156 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* eslint camelcase: ["error", { properties: "never", allow: ["^script_"] }] */ + +const St = imports.gi.St; + +const Main = imports.ui.main; +const MessageTray = imports.ui.messageTray; +const Scripting = imports.ui.scripting; + +// This script tests the most important (basic) functionality of the shell. + +export var METRICS = {}; + +export async function run() { + console.debug('Running basic perf test'); + + /* eslint-disable no-await-in-loop */ + Scripting.defineScriptEvent('topBarNavStart', 'Starting to navigate the top bar'); + Scripting.defineScriptEvent('topBarNavDone', 'Done navigating the top bar'); + Scripting.defineScriptEvent('notificationShowStart', 'Showing a notification'); + Scripting.defineScriptEvent('notificationShowDone', 'Done showing a notification'); + Scripting.defineScriptEvent('notificationCloseStart', 'Closing a notification'); + Scripting.defineScriptEvent('notificationCloseDone', 'Done closing a notification'); + Scripting.defineScriptEvent('overviewShowStart', 'Starting to show the overview'); + Scripting.defineScriptEvent('overviewShowDone', 'Overview finished showing'); + Scripting.defineScriptEvent('applicationsShowStart', 'Starting to switch to applications view'); + Scripting.defineScriptEvent('applicationsShowDone', 'Done switching to applications view'); + + Main.overview.connect('shown', + () => Scripting.scriptEvent('overviewShowDone')); + + await Scripting.sleep(1000); + + // navigate through top bar + + console.debug('Navigate through top bar'); + Scripting.scriptEvent('topBarNavStart'); + Main.panel.statusArea.quickSettings.menu.open(); + await Scripting.sleep(400); + + const { menuManager } = Main.panel; + while (menuManager.activeMenu && + Main.panel.navigate_focus(menuManager.activeMenu.sourceActor, + St.DirectionType.TAB_BACKWARD, false)) + await Scripting.sleep(400); + Scripting.scriptEvent('topBarNavDone'); + + await Scripting.sleep(1000); + + // notification + console.debug('Show notification message'); + const source = new MessageTray.SystemNotificationSource(); + Main.messageTray.add(source); + + Scripting.scriptEvent('notificationShowStart'); + source.connect('notification-show', + () => Scripting.scriptEvent('notificationShowDone')); + + const notification = new MessageTray.Notification(source, + 'A test notification'); + source.showNotification(notification); + await Scripting.sleep(400); + + console.debug('Show date menu'); + Main.panel.statusArea.dateMenu.menu.open(); + await Scripting.sleep(400); + + Scripting.scriptEvent('notificationCloseStart'); + notification.connect('destroy', + () => Scripting.scriptEvent('notificationCloseDone')); + + console.debug('Destroy notification message'); + notification.destroy(); + await Scripting.sleep(400); + + console.debug('Close date menu'); + Main.panel.statusArea.dateMenu.menu.close(); + await Scripting.waitLeisure(); + + await Scripting.sleep(1000); + + // overview (window picker) + Scripting.scriptEvent('overviewShowStart'); + Main.overview.show(); + await Scripting.waitLeisure(); + Main.overview.hide(); + await Scripting.waitLeisure(); + + await Scripting.sleep(1000); + + // overview (app picker) + console.debug('Showing overview'); + Main.overview.show(); + await Scripting.waitLeisure(); + + Scripting.scriptEvent('applicationsShowStart'); + console.debug('Showing applications'); + // eslint-disable-next-line require-atomic-updates + Main.overview.dash.showAppsButton.checked = true; + await Scripting.waitLeisure(); + Scripting.scriptEvent('applicationsShowDone'); + console.debug('Hiding applications'); + // eslint-disable-next-line require-atomic-updates + Main.overview.dash.showAppsButton.checked = false; + await Scripting.waitLeisure(); + + console.debug('Hiding overview'); + Main.overview.hide(); + await Scripting.waitLeisure(); + /* eslint-enable no-await-in-loop */ + + console.debug('Finished basic perf test'); +} + +let topBarNav = false; +let notificationShown = false; +let notificationClosed = false; +let windowPickerShown = false; +let appPickerShown = false; + +export function script_topBarNavDone() { + topBarNav = true; +} + +export function script_notificationShowDone() { + notificationShown = true; +} + +export function script_notificationCloseDone() { + notificationClosed = true; +} + +export function script_overviewShowDone() { + windowPickerShown = true; +} + +export function script_applicationsShowDone() { + appPickerShown = true; +} + +export function finish() { + if (!topBarNav) + throw new Error('Failed to navigate top bar'); + + if (!notificationShown) + throw new Error('Failed to show notification'); + + if (!notificationClosed) + throw new Error('Failed to close notification'); + + if (!windowPickerShown) + throw new Error('Failed to show window picker'); + + if (!appPickerShown) + throw new Error('Failed to show app picker'); +} diff --git a/tests/shell/closeWithActiveWindows.js b/tests/shell/closeWithActiveWindows.js new file mode 100644 index 000000000..2d696792c --- /dev/null +++ b/tests/shell/closeWithActiveWindows.js @@ -0,0 +1,35 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^malloc", "^glx", "^clutter"] }] */ + +const Clutter = imports.gi.Clutter; + +const Main = imports.ui.main; +const Scripting = imports.ui.scripting; + +/** Run test. */ +export async function run() { + /* eslint-disable no-await-in-loop */ + + /* Make created windows remain visible during exit. */ + await Scripting.disableHelperAutoExit(); + + const seat = Clutter.get_default_backend().get_default_seat(); + const virtualDevice_ = + seat.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE); + + Main.overview.hide(); + await Scripting.waitLeisure(); + await Scripting.sleep(1000); + + await Scripting.createTestWindow({ + width: 640, + height: 480, + textInput: true, + }); + + await Scripting.waitTestWindows(); + await Scripting.waitLeisure(); + await Scripting.sleep(1000); + + /* eslint-enable no-await-in-loop */ +} diff --git a/tests/shell/core.js b/tests/shell/core.js new file mode 100644 index 000000000..78d17d8c9 --- /dev/null +++ b/tests/shell/core.js @@ -0,0 +1,267 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^malloc", "^glx", "^clutter"] }] */ + +const System = imports.system; + +const Main = imports.ui.main; +const Scripting = imports.ui.scripting; + +// This performance script measure the most important (core) performance +// metrics for the shell. By looking at the output metrics of this script +// someone should be able to get an idea of how well the shell is performing +// on a particular system. + +export var METRICS = { + overviewLatencyFirst: { + description: 'Time to first frame after triggering overview, first time', + units: 'us', + }, + overviewFpsFirst: { + description: 'Frame rate when going to the overview, first time', + units: 'frames / s', + }, + overviewLatencySubsequent: { + description: 'Time to first frame after triggering overview, second time', + units: 'us', + }, + overviewFpsSubsequent: { + description: 'Frames rate when going to the overview, second time', + units: 'frames / s', + }, + overviewFps5Windows: { + description: 'Frames rate when going to the overview, 5 windows open', + units: 'frames / s', + }, + overviewFps10Windows: { + description: 'Frames rate when going to the overview, 10 windows open', + units: 'frames / s', + }, + overviewFps5Maximized: { + description: 'Frames rate when going to the overview, 5 maximized windows open', + units: 'frames / s', + }, + overviewFps10Maximized: { + description: 'Frames rate when going to the overview, 10 maximized windows open', + units: 'frames / s', + }, + overviewFps5Alpha: { + description: 'Frames rate when going to the overview, 5 alpha-transparent windows open', + units: 'frames / s', + }, + overviewFps10Alpha: { + description: 'Frames rate when going to the overview, 10 alpha-transparent windows open', + units: 'frames / s', + }, + usedAfterOverview: { + description: "Malloc'ed bytes after the overview is shown once", + units: 'B', + }, + leakedAfterOverview: { + description: "Additional malloc'ed bytes the second time the overview is shown", + units: 'B', + }, + applicationsShowTimeFirst: { + description: 'Time to switch to applications view, first time', + units: 'us', + }, + applicationsShowTimeSubsequent: { + description: 'Time to switch to applications view, second time', + units: 'us', + }, +}; + +const WINDOW_CONFIGS = [{ + width: 640, height: 480, + alpha: false, maximized: false, count: 1, metric: 'overviewFpsSubsequent', +}, { + width: 640, height: 480, + alpha: false, maximized: false, count: 5, metric: 'overviewFps5Windows', +}, { + width: 640, height: 480, + alpha: false, maximized: false, count: 10, metric: 'overviewFps10Windows', +}, { + width: 640, height: 480, + alpha: false, maximized: true, count: 5, metric: 'overviewFps5Maximized', +}, { + width: 640, height: 480, + alpha: false, maximized: true, count: 10, metric: 'overviewFps10Maximized', +}, { + width: 640, height: 480, + alpha: true, maximized: false, count: 5, metric: 'overviewFps5Alpha', +}, { + width: 640, height: 480, + alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha', +}]; + +export async function run() { + /* eslint-disable no-await-in-loop */ + Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview"); + Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing"); + Scripting.defineScriptEvent("afterShowHide", "After a show/hide cycle for the overview"); + Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view"); + Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view"); + + // Enable recording of timestamps for different points in the frame cycle + global.frame_timestamps = true; + + Main.overview.connect('shown', () => { + Scripting.scriptEvent('overviewShowDone'); + }); + + await Scripting.sleep(1000); + + for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) { + // We go to the overview twice for each configuration; the first time + // to calculate the mipmaps for the windows, the second time to get + // a clean set of numbers. + if ((i % 2) == 0) { + let config = WINDOW_CONFIGS[i / 2]; + await Scripting.destroyTestWindows(); + + for (let k = 0; k < config.count; k++) { + await Scripting.createTestWindow({ + width: config.width, + height: config.height, + alpha: config.alpha, + maximized: config.maximized, + }); + } + + await Scripting.waitTestWindows(); + await Scripting.sleep(1000); + await Scripting.waitLeisure(); + } + + Scripting.scriptEvent('overviewShowStart'); + Main.overview.show(); + + await Scripting.waitLeisure(); + Main.overview.hide(); + await Scripting.waitLeisure(); + + System.gc(); + await Scripting.sleep(1000); + Scripting.collectStatistics(); + Scripting.scriptEvent('afterShowHide'); + } + + await Scripting.destroyTestWindows(); + await Scripting.sleep(1000); + + Main.overview.show(); + await Scripting.waitLeisure(); + + for (let i = 0; i < 2; i++) { + Scripting.scriptEvent('applicationsShowStart'); + // eslint-disable-next-line require-atomic-updates + Main.overview.dash.showAppsButton.checked = true; + await Scripting.waitLeisure(); + Scripting.scriptEvent('applicationsShowDone'); + // eslint-disable-next-line require-atomic-updates + Main.overview.dash.showAppsButton.checked = false; + await Scripting.waitLeisure(); + } + /* eslint-enable no-await-in-loop */ +} + +let showingOverview = false; +let finishedShowingOverview = false; +let overviewShowStart; +let overviewFrames; +let overviewLatency; +let mallocUsedSize = 0; +let overviewShowCount = 0; +let haveSwapComplete = false; +let applicationsShowStart; +let applicationsShowCount = 0; + +export function script_overviewShowStart(time) { + showingOverview = true; + finishedShowingOverview = false; + overviewShowStart = time; + overviewFrames = 0; +} + +export function script_overviewShowDone(_time) { + // We've set up the state at the end of the zoom out, but we + // need to wait for one more frame to paint before we count + // ourselves as done. + finishedShowingOverview = true; +} + +export function script_applicationsShowStart(time) { + applicationsShowStart = time; +} + +export function script_applicationsShowDone(time) { + applicationsShowCount++; + if (applicationsShowCount == 1) + METRICS.applicationsShowTimeFirst.value = time - applicationsShowStart; + else + METRICS.applicationsShowTimeSubsequent.value = time - applicationsShowStart; +} + +export function script_afterShowHide(_time) { + if (overviewShowCount == 1) + METRICS.usedAfterOverview.value = mallocUsedSize; + else + METRICS.leakedAfterOverview.value = mallocUsedSize - METRICS.usedAfterOverview.value; +} + +export function malloc_usedSize(time, bytes) { + mallocUsedSize = bytes; +} + +function _frameDone(time) { + if (showingOverview) { + if (overviewFrames == 0) + overviewLatency = time - overviewShowStart; + + overviewFrames++; + } + + if (finishedShowingOverview) { + showingOverview = false; + finishedShowingOverview = false; + overviewShowCount++; + + let dt = (time - (overviewShowStart + overviewLatency)) / 1000000; + + // If we see a start frame and an end frame, that would + // be 1 frame for a FPS computation, hence the '- 1' + let fps = (overviewFrames - 1) / dt; + + if (overviewShowCount == 1) { + METRICS.overviewLatencyFirst.value = overviewLatency; + METRICS.overviewFpsFirst.value = fps; + } else if (overviewShowCount == 2) { + METRICS.overviewLatencySubsequent.value = overviewLatency; + } + + // Other than overviewFpsFirst, we collect FPS metrics the second + // we show each window configuration. overviewShowCount is 1,2,3... + if (overviewShowCount % 2 == 0) { + let config = WINDOW_CONFIGS[(overviewShowCount / 2) - 1]; + METRICS[config.metric].value = fps; + } + } +} + +export function glx_swapComplete(time, swapTime) { + haveSwapComplete = true; + + _frameDone(swapTime); +} + +export function clutter_stagePaintDone(time) { + // If we aren't receiving GLXBufferSwapComplete events, then we approximate + // the time the user sees a frame with the time we finished doing drawing + // commands for the frame. This doesn't take into account the time for + // the GPU to finish painting, and the time for waiting for the buffer + // swap, but if this are uniform - every frame takes the same time to draw - + // then it won't upset our FPS calculation, though the latency value + // will be slightly too low. + + if (!haveSwapComplete) + _frameDone(time); +} diff --git a/tests/shell/headlessStart.js b/tests/shell/headlessStart.js new file mode 100644 index 000000000..6f4157b2b --- /dev/null +++ b/tests/shell/headlessStart.js @@ -0,0 +1,107 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* eslint camelcase: ["error", { properties: "never", allow: ["^script_"] }] */ + +const GLib = imports.gi.GLib; +const MetaTest = imports.gi.MetaTest; +const Shell = imports.gi.Shell; + +const Main = imports.ui.main; +const Scripting = imports.ui.scripting; + +// This script tests that the shell handles connecting monitors after startup +// is properly handled. + +export var METRICS = {}; + +let _testMonitor = null; + +/** + * init: + */ +export function init() { + global.connect('shutdown', () => { + _testMonitor?.destroy(); + }); + GLib.timeout_add_seconds( + GLib.PRIORITY_LOW, 2, + () => { + log('Connecting 1280x720 test monitor'); + _testMonitor = MetaTest.TestMonitor.new( + global.context, 1280, 720, 60.0); + }); + Scripting.defineScriptEvent('monitorsChanged', 'Monitors changed'); + + const display = global.display; + console.assert(display.get_n_monitors() === 0, 'Not running headless'); + + const monitorManager = global.backend.get_monitor_manager(); + const monitorsChangedHandlerId = monitorManager.connect('monitors-changed', + () => { + console.assert(display.get_n_monitors() === 1, + 'Expected a single monitor'); + Scripting.scriptEvent('monitorsChanged'); + monitorManager.disconnect(monitorsChangedHandlerId); + }); + Shell.PerfLog.get_default().set_enabled(true); +} + +/** + * run: + */ +export async function run() { + /* eslint-disable no-await-in-loop */ + Scripting.defineScriptEvent('overviewShowDone', 'Overview finished showing'); + Scripting.defineScriptEvent('overviewHideDone', 'Overview finished hiding'); + + Main.overview.connect('shown', + () => Scripting.scriptEvent('overviewShowDone')); + Main.overview.connect('hidden', + () => Scripting.scriptEvent('overviewHideDone')); + + Main.overview.hide(); + await Scripting.waitLeisure(); + + Main.overview.show(); + await Scripting.waitLeisure(); + + /* eslint-enable no-await-in-loop */ +} + +let monitorsChanged = false; +let overviewHidden = false; +let overviewShown = false; + +/** + * script_monitorsChanged: + */ +export function script_monitorsChanged() { + monitorsChanged = true; +} + +/** + * script_overviewHideDone: + */ +export function script_overviewHideDone() { + overviewHidden = true; +} + +/** + * script_overviewShowDone: + */ +export function script_overviewShowDone() { + overviewShown = true; +} + +/** + * finish: + */ +export function finish() { + if (!monitorsChanged) + throw new Error('Monitors never changed'); + + if (!overviewHidden) + throw new Error('Failed to hide overview'); + + if (!overviewShown) + throw new Error('Failed to show overview'); +} diff --git a/tests/shell/hwtest.js b/tests/shell/hwtest.js new file mode 100644 index 000000000..6e4cd7ea2 --- /dev/null +++ b/tests/shell/hwtest.js @@ -0,0 +1,313 @@ +/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^clutter"] }] */ +const Clutter = imports.gi.Clutter; +const Gio = imports.gi.Gio; +const Shell = imports.gi.Shell; +const Main = imports.ui.main; +const Scripting = imports.ui.scripting; + +export var METRICS = { + timeToDesktop: { + description: 'Time from starting graphical.target to desktop showing', + units: 'us', + }, + + overviewShowTime: { + description: 'Time to switch to overview view, first time', + units: 'us', + }, + + applicationsShowTime: { + description: 'Time to switch to applications view, first time', + units: 'us', + }, + + mainViewRedrawTime: { + description: 'Time to redraw the main view, full screen', + units: 'us', + }, + + overviewRedrawTime: { + description: 'Time to redraw the overview, full screen, 5 windows', + units: 'us', + }, + + applicationRedrawTime: { + description: 'Time to redraw frame with a maximized application update', + units: 'us', + }, + + geditStartTime: { + description: 'Time from gedit launch to window drawn', + units: 'us', + }, +}; + +function waitAndDraw(milliseconds) { + let cb; + + let timeline = new Clutter.Timeline({ duration: milliseconds }); + timeline.start(); + + timeline.connect('new-frame', (_timeline, _frame) => { + global.stage.queue_redraw(); + }); + + timeline.connect('completed', () => { + timeline.stop(); + if (cb) + cb(); + }); + + return callback => (cb = callback); +} + +function waitSignal(object, signal) { + let cb; + + let id = object.connect(signal, () => { + object.disconnect(id); + if (cb) + cb(); + }); + + return callback => (cb = callback); +} + +function extractBootTimestamp() { + const sp = Gio.Subprocess.new([ + 'journalctl', '-b', + 'MESSAGE_ID=7d4958e842da4a758f6c1cdc7b36dcc5', + 'UNIT=graphical.target', + '-o', + 'json', + ], Gio.SubprocessFlags.STDOUT_PIPE); + let result = null; + + let datastream = Gio.DataInputStream.new(sp.get_stdout_pipe()); + while (true) { // eslint-disable-line no-constant-condition + let [line, length_] = datastream.read_line_utf8(null); + if (line === null) + break; + + let fields = JSON.parse(line); + result = Number(fields['__MONOTONIC_TIMESTAMP']); + } + datastream.close(null); + return result; +} + +export async function run() { + /* eslint-disable no-await-in-loop */ + Scripting.defineScriptEvent("desktopShown", "Finished initial animation"); + Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview"); + Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing"); + Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view"); + Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view"); + Scripting.defineScriptEvent("mainViewDrawStart", "Drawing main view"); + Scripting.defineScriptEvent("mainViewDrawDone", "Ending timing main view drawing"); + Scripting.defineScriptEvent("overviewDrawStart", "Drawing overview"); + Scripting.defineScriptEvent("overviewDrawDone", "Ending timing overview drawing"); + Scripting.defineScriptEvent("redrawTestStart", "Drawing application window"); + Scripting.defineScriptEvent("redrawTestDone", "Ending timing application window drawing"); + Scripting.defineScriptEvent("collectTimings", "Accumulate frame timings from redraw tests"); + Scripting.defineScriptEvent("geditLaunch", "gedit application launch"); + Scripting.defineScriptEvent("geditFirstFrame", "first frame of gedit window drawn"); + + await Scripting.waitLeisure(); + Scripting.scriptEvent('desktopShown'); + + let interfaceSettings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.interface', + }); + interfaceSettings.set_boolean('enable-animations', false); + + Scripting.scriptEvent('overviewShowStart'); + Main.overview.show(); + await Scripting.waitLeisure(); + Scripting.scriptEvent('overviewShowDone'); + + await Scripting.sleep(1000); + + Scripting.scriptEvent('applicationsShowStart'); + // eslint-disable-next-line require-atomic-updates + Main.overview.dash.showAppsButton.checked = true; + + await Scripting.waitLeisure(); + Scripting.scriptEvent('applicationsShowDone'); + + await Scripting.sleep(1000); + + Main.overview.hide(); + await Scripting.waitLeisure(); + + // --------------------- // + // Tests of redraw speed // + // --------------------- // + + global.frame_timestamps = true; + global.frame_finish_timestamp = true; + + for (let k = 0; k < 5; k++) + await Scripting.createTestWindow({ maximized: true }); + await Scripting.waitTestWindows(); + + await Scripting.sleep(1000); + + Scripting.scriptEvent('mainViewDrawStart'); + await waitAndDraw(1000); + Scripting.scriptEvent('mainViewDrawDone'); + + Main.overview.show(); + Scripting.waitLeisure(); + + await Scripting.sleep(1500); + + Scripting.scriptEvent('overviewDrawStart'); + await waitAndDraw(1000); + Scripting.scriptEvent('overviewDrawDone'); + + await Scripting.destroyTestWindows(); + Main.overview.hide(); + + await Scripting.createTestWindow({ + maximized: true, + redraws: true, + }); + await Scripting.waitTestWindows(); + + await Scripting.sleep(1000); + + Scripting.scriptEvent('redrawTestStart'); + await Scripting.sleep(1000); + Scripting.scriptEvent('redrawTestDone'); + + await Scripting.sleep(1000); + Scripting.scriptEvent('collectTimings'); + + await Scripting.destroyTestWindows(); + + global.frame_timestamps = false; + global.frame_finish_timestamp = false; + + await Scripting.sleep(1000); + + let appSys = Shell.AppSystem.get_default(); + let app = appSys.lookup_app('org.gnome.gedit.desktop'); + + Scripting.scriptEvent('geditLaunch'); + app.activate(); + + let windows = app.get_windows(); + if (windows.length > 0) + throw new Error('gedit was already running'); + + while (windows.length == 0) { + await waitSignal(global.display, 'window-created'); + windows = app.get_windows(); + } + + let actor = windows[0].get_compositor_private(); + await waitSignal(actor, 'first-frame'); + Scripting.scriptEvent('geditFirstFrame'); + + await Scripting.sleep(1000); + + windows[0].delete(global.get_current_time()); + + await Scripting.sleep(1000); + + interfaceSettings.set_boolean('enable-animations', true); + /* eslint-enable no-await-in-loop */ +} + +let overviewShowStart; +let applicationsShowStart; +let stagePaintStart; +let redrawTiming; +let redrawTimes = {}; +let geditLaunchTime; + +export function script_desktopShown(time) { + let bootTimestamp = extractBootTimestamp(); + METRICS.timeToDesktop.value = time - bootTimestamp; +} + +export function script_overviewShowStart(time) { + overviewShowStart = time; +} + +export function script_overviewShowDone(time) { + METRICS.overviewShowTime.value = time - overviewShowStart; +} + +export function script_applicationsShowStart(time) { + applicationsShowStart = time; +} + +export function script_applicationsShowDone(time) { + METRICS.applicationsShowTime.value = time - applicationsShowStart; +} + +export function script_mainViewDrawStart(_time) { + redrawTiming = 'mainView'; +} + +export function script_mainViewDrawDone(_time) { + redrawTiming = null; +} + +export function script_overviewDrawStart(_time) { + redrawTiming = 'overview'; +} + +export function script_overviewDrawDone(_time) { + redrawTiming = null; +} + +export function script_redrawTestStart(_time) { + redrawTiming = 'application'; +} + +export function script_redrawTestDone(_time) { + redrawTiming = null; +} + +export function script_collectTimings(_time) { + for (let timing in redrawTimes) { + let times = redrawTimes[timing]; + times.sort((a, b) => a - b); + + let len = times.length; + let median; + + if (len == 0) + median = -1; + else if (len % 2 == 1) + median = times[(len - 1) / 2]; + else + median = Math.round((times[len / 2 - 1] + times[len / 2]) / 2); + + METRICS[`${timing}RedrawTime`].value = median; + } +} + +export function script_geditLaunch(time) { + geditLaunchTime = time; +} + +export function script_geditFirstFrame(time) { + METRICS.geditStartTime.value = time - geditLaunchTime; +} + +export function clutter_stagePaintStart(time) { + stagePaintStart = time; +} + +export function clutter_paintCompletedTimestamp(time) { + if (redrawTiming != null && stagePaintStart != null) { + if (!(redrawTiming in redrawTimes)) + redrawTimes[redrawTiming] = []; + redrawTimes[redrawTiming].push(time - stagePaintStart); + } + stagePaintStart = null; +}